// Filename: textclass.cpp

#include "textclass.h"

//-------------------------------------------------------------------------------------------------------------------
TextClass::TextClass() 
	: m_Font(0), m_FontShader(0), m_sentence1(0), m_sentence2(0), m_command(0)
{ }

//-------------------------------------------------------------------------------------------------------------------
TextClass::TextClass(const TextClass& rhs)
{ }

//-------------------------------------------------------------------------------------------------------------------
TextClass::~TextClass()
{ }

//-------------------------------------------------------------------------------------------------------------------
bool TextClass::Initialize(ID3D11Device* device, ID3D11DeviceContext* deviceContext, HWND hwnd,
	int screenWidth, int screenHeight, D3DXMATRIX baseViewMatrix)
{
	bool result;
	
	m_screenWidth		= screenWidth;		// Store screen width.
	m_screenHeight		= screenHeight;		// Store screen height.
	m_baseViewMatrix	= baseViewMatrix;	// Store the base view matrix.
	
	m_Font = new FontClass;					// Create the font object.
	if (!m_Font) return false;
	
	result = m_Font->Initialize(device, 
		"bulat-research-project/graphics/fonts/arialfontdata.txt", 
		L"bulat-research-project/graphics/textures/arialfont.dds");	// Initialize the font object.

	if (!result) {
		MessageBox(hwnd, L"Could not initialize the font object.", L"Error", MB_OK);
		return false;
	}

	m_FontShader = new FontShaderClass;		// Create the font shader object.
	if (!m_FontShader)	
		return false;
	
	result = m_FontShader->Initialize(device, hwnd);		// Initialize the font shader object.
	if (!result) {
		MessageBox(hwnd, L"Could not initialize the font shader object.", L"Error", MB_OK);
		return false;
	}
	
	
	result = InitializeSentence(&m_sentence1, 32, device);	// Initialize and update the first sentence.
	if (!result) return false;
	
	result = UpdateSentence(m_sentence1, "Mouse X: ", 10, 10, 1.0f, 1.0f, 1.0f, deviceContext);
	if (!result) return false;
	
	result = InitializeSentence(&m_sentence2, 32, device);	// Initialize and update the second sentence.
	if (!result) return false;
	
	result = UpdateSentence(m_sentence2, "Mouse Y: ", 10, 30, 0.0f, 1.0f, 0.0f, deviceContext);
	if (!result) return false;

	result = InitializeSentence(&m_command, 128, device);
	if (!result) return false;
	
	// Start command prompt with a space.
	result = UpdateSentence(m_command, " ", 10, (768 - 20), 0.0f, 1.0f, 0.0f, deviceContext);
	if (!result) return false;

	return true;
}

//-------------------------------------------------------------------------------------------------------------------
void TextClass::Shutdown()
{
	ReleaseSentence(&m_sentence1);		// Release the first sentence
	ReleaseSentence(&m_sentence2);		// Release the second sentence.
	ReleaseSentence(&m_command);		// Release the command sentence.
	
	if (m_FontShader) {					// Release the font shader object.
		m_FontShader->Shutdown();
		delete m_FontShader;
		m_FontShader = nullptr;
	}
	
	if (m_Font) {						// Release the font object.
		m_Font->Shutdown();
		delete m_Font;
		m_Font = nullptr;
	}
}

//-------------------------------------------------------------------------------------------------------------------
bool TextClass::Render(ID3D11DeviceContext* deviceContext, D3DXMATRIX orthoMatrix)
{
	D3DXMATRIX world;
	bool result;
	
	// Set up and pass in world matrices to position the sentences on the screen.

	D3DXMatrixIdentity(&world);
	result = RenderSentence(deviceContext, m_sentence1, world, orthoMatrix);	// Draw the first sentence.
	if (!result)
		return false;
	
	result = RenderSentence(deviceContext, m_sentence2, world, orthoMatrix);	// Draw the second sentence.
	if (!result)
		return false;

	D3DXMatrixTranslation(&world, 72.0f, 0.0f, 0.0f);
	result = RenderSentence(deviceContext, m_command, world, orthoMatrix);	// Draw command sentence.

	return true;
}

//-------------------------------------------------------------------------------------------------------------------
bool TextClass::InitializeSentence(SentenceType** sentence, int maxLength, ID3D11Device* device)
{
	VertexType*				vertices;							// Vertex array to put in vertex buffer.
	unsigned long*			indices;							// Index array to put in the index buffer.
	D3D11_BUFFER_DESC		vertexBufferDesc, indexBufferDesc;	// Buffer descriptions for vertex and index buffers.
	D3D11_SUBRESOURCE_DATA	vertexData, indexData;				// Vertex and index buffer resource data.
	HRESULT					result;
	int						i;
	
	*sentence = new SentenceType;	// Create a new sentence object.
	if (!*sentence)
		return false;
	
	(*sentence)->vertexBuffer = nullptr;					// Initialize the sentence buffers to null.
	(*sentence)->indexBuffer  = nullptr;
	(*sentence)->maxLength	  = maxLength;					// Set the maximum length of the sentence.
	(*sentence)->vertexCount  = 6 * maxLength;				// Set the number of vertices in the vertex array.
	(*sentence)->indexCount   = (*sentence)->vertexCount;	// Set the number of indices in the index array.
	
	vertices = new VertexType[(*sentence)->vertexCount];	// Create the vertex array.
	if (!vertices)
		return false;
	
	indices = new unsigned long[(*sentence)->indexCount];	// Create the index array.
	if (!indices)
		return false;
	
	memset(vertices, 0, (sizeof(VertexType) * (*sentence)->vertexCount)); // Initialize the vertex array to zero.
	
	for (i = 0; i < (*sentence)->indexCount; ++i)	// Initialize the index array
		indices[i] = static_cast<unsigned long>(i);

	// Dynamic vertex buffer description.
	vertexBufferDesc.Usage				 = D3D11_USAGE_DYNAMIC;
	vertexBufferDesc.ByteWidth			 = sizeof(VertexType) * (*sentence)->vertexCount;
	vertexBufferDesc.BindFlags			 = D3D11_BIND_VERTEX_BUFFER;
	vertexBufferDesc.CPUAccessFlags		 = D3D11_CPU_ACCESS_WRITE;
	vertexBufferDesc.MiscFlags			 = 0;
	vertexBufferDesc.StructureByteStride = 0;
	
	vertexData.pSysMem					 = vertices;		// Set resource data.
	vertexData.SysMemPitch				 = 0;
	vertexData.SysMemSlicePitch			 = 0;
	
	result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &(*sentence)->vertexBuffer);	// Create the vertex buffer.
	if (FAILED(result))
		return false;

	// Static index buffer description.
	indexBufferDesc.Usage				 = D3D11_USAGE_DEFAULT;
	indexBufferDesc.ByteWidth			 = sizeof(unsigned long) * (*sentence)->indexCount;
	indexBufferDesc.BindFlags			 = D3D11_BIND_INDEX_BUFFER;
	indexBufferDesc.CPUAccessFlags		 = 0;
	indexBufferDesc.MiscFlags			 = 0;
	indexBufferDesc.StructureByteStride  = 0;
	
	indexData.pSysMem					 = indices;	// Set resource data.
	indexData.SysMemPitch				 = 0;
	indexData.SysMemSlicePitch			 = 0;
	
	result = device->CreateBuffer(&indexBufferDesc, &indexData, &(*sentence)->indexBuffer);		// Create the index buffer.
	if (FAILED(result))
		return false;

	delete [] vertices;		// Release the vertex array.
	vertices = nullptr;

	delete [] indices;		// Release the index array.
	indices = nullptr;

	return true;
}

//-------------------------------------------------------------------------------------------------------------------
bool TextClass::UpdateSentence(SentenceType* sentence, char* text, int positionX, int positionY, float red,
	float green, float blue, ID3D11DeviceContext* deviceContext)
{
	int							numLetters;				// Number of new letters to create.
	VertexType*					vertices;				// New vertex array for vertex buffer.
	float						drawX, drawY;			// New screen position for sentence.
	HRESULT						result;					// Result variable.
	D3D11_MAPPED_SUBRESOURCE	mappedResource;			// Resource data for sentences vertex buffer.
	VertexType*					verticesPtr;			// Pointer to vertex buffer.
	
	sentence->red		= red;							// Store the colour of the sentence.
	sentence->green		= green;
	sentence->blue		= blue;
	numLetters			= (int)strlen(text);			// Get the number of letters in the sentence.
	
	if (numLetters > sentence->maxLength)				// Check for possible buffer overflow.
		return false;
	
	vertices = new VertexType[sentence->vertexCount];	// Create the vertex array.
	if (!vertices)
		return false;
	
	memset(vertices, 0, (sizeof(VertexType) * sentence->vertexCount));	// Initialize vertex array to zero.
	
	drawX = (float)(((m_screenWidth / 2) * -1) + positionX);			// Calculate X pixel position to start drawing.
	drawY = (float)((m_screenHeight / 2) - positionY);					// Calculate Y pixel position to start drawing.

	m_Font->BuildVertexArray((void*)vertices, text, drawX, drawY);		// Build sentence array from text and draw location.
	
	result = deviceContext->Map(sentence->vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	if (FAILED(result))	// Lock the vertex buffer so it can be written to.
		return false;
	
	verticesPtr = (VertexType*)mappedResource.pData;				// Get a pointer to the data in the vertex buffer.
	memcpy(verticesPtr, (void*)vertices, (sizeof(VertexType) * sentence->vertexCount));	// Copy data into the vertex buffer.
	deviceContext->Unmap(sentence->vertexBuffer, 0);				// Unlock the vertex buffer.
	
	delete [] vertices;		// Release the vertex array.
	vertices = nullptr;

	return true;
}

//-------------------------------------------------------------------------------------------------------------------
void TextClass::ReleaseSentence(SentenceType** sentence)
{
	if (*sentence)
	{
		if ((*sentence)->vertexBuffer)		// Release the sentence vertex buffer.
		{
			(*sentence)->vertexBuffer->Release();
			(*sentence)->vertexBuffer = nullptr;
		}

		if ((*sentence)->indexBuffer)		// Release the sentence index buffer.
		{
			(*sentence)->indexBuffer->Release();
			(*sentence)->indexBuffer = nullptr;
		}
		
		delete (*sentence);		// Release the sentence.
		*sentence = nullptr;
	}
}

//-------------------------------------------------------------------------------------------------------------------
bool TextClass::RenderSentence(ID3D11DeviceContext* deviceContext, SentenceType* sentence, D3DXMATRIX worldMatrix,
	D3DXMATRIX orthoMatrix)
{
	unsigned int	stride, offset;
	D3DXVECTOR4		pixelColor;
	bool			result;
	
	stride = sizeof(VertexType);	// Set the vertex buffer stride and offset.
	offset = 0;
	
	deviceContext->IASetVertexBuffers(0, 1, &sentence->vertexBuffer, &stride, &offset);	// Set the vertex buffer to active in the input assembler.
	deviceContext->IASetIndexBuffer(sentence->indexBuffer, DXGI_FORMAT_R32_UINT, 0);	// Set the index buffer to active in the input assembler.
	deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);		// Set the type of primitive that should be rendered from this vertex buffer.
	
	pixelColor = D3DXVECTOR4(sentence->red, sentence->green, sentence->blue, 1.0f);		// Create a pixel color vector with the input sentence color.
	
	result = m_FontShader->Render(	// Render the text using the font shader.
		deviceContext, sentence->indexCount, worldMatrix, m_baseViewMatrix, orthoMatrix, m_Font->GetTexture(), pixelColor);

	if (!result)
		return false;

	return true;
}

//-------------------------------------------------------------------------------------------------------------------
bool TextClass::SetMousePosition(int mouseX, int mouseY, ID3D11DeviceContext* deviceContext)
{
	char	tempString[16];
	char	mouseString[16];
	bool	result;

	// Convert the mouseX integer to a string format.
	_itoa_s(mouseX, tempString, 10);

	// Setup the mouseX string.
	strcpy_s(mouseString, "Mouse X: ");
	strcat_s(mouseString, tempString);

	// Update the sentence vertex buffer with the new string information.
	result = UpdateSentence(m_sentence1, mouseString, 10, 10, 1.0f, 1.0f, 1.0f, deviceContext);
	if (!result) return false;

	// Convert the mouseY integer to string format.
	_itoa_s(mouseY, tempString, 10);

	// Setup the mouseY string.
	strcpy_s(mouseString, "Mouse Y: ");
	strcat_s(mouseString, tempString);

	// Update the sentence vertex buffer with the new string information.
	result = UpdateSentence(m_sentence2, mouseString, 10, 30, 1.0f, 1.0f, 1.0f, deviceContext);
	if (!result) return false;

	return true;
}

//-------------------------------------------------------------------------------------------------------------------
bool TextClass::SetCommandLine(std::vector<char>& inputb, ID3D11DeviceContext* deviceContext)
{
	bool result;
	char buffer[128];
	UINT i;

	UINT length = inputb.size();

	if (length > 127)	// Check for buffer overflow
		length = 127;

	for (i = 0; i < length; ++i)
	{
		buffer[i] = inputb[i];
	}
	
	buffer[length] = '\0';		// Add null terminator.

	result = UpdateSentence(m_command, buffer, 10, (768 - 20), 0.0f, 1.0f, 0.0f, deviceContext);
	if (!result)
		return false;

	return true;
}